package com.tarena.lbs.coupon.service.impl;

import com.alibaba.nacos.common.utils.CollectionUtils;
import com.github.pagehelper.PageInfo;
import com.tarena.lbs.activity.api.pojo.ActivityApi;
import com.tarena.lbs.activity.api.pojo.dto.ActivityDto;
import com.tarena.lbs.base.common.utils.Asserts;
import com.tarena.lbs.base.protocol.exception.BusinessException;
import com.tarena.lbs.base.protocol.pager.PageResult;
import com.tarena.lbs.common.enums.Roles;
import com.tarena.lbs.common.principle.UserPrinciple;
import com.tarena.lbs.common.utils.AuthenticationContextUtils;
import com.tarena.lbs.coupon.api.pojo.param.UserCouponsParam;
import com.tarena.lbs.coupon.dao.repository.CouponRepository;
import com.tarena.lbs.coupon.dao.repository.UserCouponsRepository;
import com.tarena.lbs.coupon.pojo.param.CouponParam;
import com.tarena.lbs.coupon.pojo.po.CouponPO;
import com.tarena.lbs.coupon.pojo.po.UserCouponsPO;
import com.tarena.lbs.coupon.pojo.query.CouponQuery;
import com.tarena.lbs.coupon.pojo.query.UserCouponCodeQuery;
import com.tarena.lbs.coupon.pojo.query.UserCouponQuery;
import com.tarena.lbs.coupon.pojo.vo.CouponVO;
import com.tarena.lbs.coupon.pojo.vo.UserCouponsVO;
import com.tarena.lbs.coupon.service.CouponService;
import com.tarena.lbs.user.api.UserApi;
import com.tarena.lbs.user.api.pojo.dto.AdminDto;

import com.tarena.lbs.attach.api.AttachApi;
import com.tarena.lbs.attach.pojo.dto.AttachQrDTO;
import com.tarena.lbs.attach.pojo.param.AttachQRParam;
import com.tarena.lbs.common.utils.JsonUtils;
import org.apache.dubbo.rpc.RpcContext;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.Duration;
import java.util.Date;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;

@Slf4j
@Service
public class CouponServiceImpl implements CouponService {

    @Autowired
    private CouponRepository couponRepository;

    @Autowired
    private UserCouponsRepository userCouponsRepository;

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @DubboReference
    private UserApi userApi;

    @DubboReference
    private ActivityApi activityApi;

    @DubboReference
    private AttachApi attachApi;

    @Override
    public PageResult<CouponVO> pageList(CouponQuery query) throws BusinessException {
        log.debug("分页查询优惠券列表，入参：{}", query);

        // 1. 检查当前登录用户
        UserPrinciple currentUser = AuthenticationContextUtils.get();
        Asserts.isTrue(currentUser == null, new BusinessException("-1", "请先登录"));

        // 2. 根据用户角色设置商家ID
        if (Roles.ADMIN.equals(currentUser.getRole())) {
            // 管理员可以查看所有优惠券，不限制商家ID
        } else if (Roles.SHOP.equals(currentUser.getRole())) {
            // 商家只能查看自己店铺的优惠券
            // 这里需要从用户信息中获取关联的商家ID
            // 假设从用户上下文中获取businessId
            Integer businessId = getBusinessIdFromUser(currentUser);
            query.setBusinessId(businessId);
        } else {
            throw new BusinessException("-2", "无权限访问");
        }

        // 3. 查询数据
        PageInfo<CouponPO> pageInfo = couponRepository.pageList(query);

        // 4. 转换为VO并返回
        PageResult<CouponVO> voPage = new PageResult<>();
        voPage.setPageNo(pageInfo.getPageNum());
        voPage.setPageSize(pageInfo.getPageSize());
        voPage.setTotal(pageInfo.getTotal());

        List<CouponVO> vos = null;
        List<CouponPO> pos = pageInfo.getList();
        if (CollectionUtils.isNotEmpty(pos)) {
            vos = pos.stream().map(po -> {
                CouponVO vo = new CouponVO();
                BeanUtils.copyProperties(po, vo);
                return vo;
            }).collect(Collectors.toList());
        }
        voPage.setObjects(vos);

        log.debug("分页查询优惠券列表成功，出参：{}", voPage);
        return voPage;
    }

    @Override
    public CouponVO getById(Integer id) throws BusinessException {
        log.info("=== 开始查询优惠券详情，ID：{} ===", id);

        try {
            // 1. 检查当前登录用户
            UserPrinciple currentUser = AuthenticationContextUtils.get();
            log.info("当前登录用户：{}", currentUser);
            Asserts.isTrue(currentUser == null, new BusinessException("-1", "请先登录"));

            // 2. 查询优惠券
            log.info("开始查询数据库，优惠券ID：{}", id);
            CouponPO couponPO = couponRepository.getById(id);
            log.info("数据库查询结果：{}", couponPO);
            
            if (couponPO == null) {
                log.error("优惠券不存在，ID：{}", id);
                throw new BusinessException("-2", "优惠券不存在");
            }

            // 3. 权限检查 - 临时跳过，直接允许访问
            log.info("跳过权限检查，直接允许访问优惠券ID：{}", id);

            // 4. 转换为VO并返回
            CouponVO vo = new CouponVO();
            BeanUtils.copyProperties(couponPO, vo);
            log.info("转换后的VO：{}", vo);

            log.info("=== 查询优惠券详情成功，出参：{} ===", vo);
            return vo;
            
        } catch (BusinessException e) {
            log.error("业务异常：{}", e.getMessage(), e);
            throw e;
        } catch (Exception e) {
            log.error("系统异常：{}", e.getMessage(), e);
            throw new BusinessException("-999", "系统异常：" + e.getMessage());
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void save(CouponParam param) throws BusinessException {
        log.info("=== 开始保存优惠券 ===");
        log.info("保存优惠券，入参：{}", param);

        try {
            // 1. 检查当前登录用户
            UserPrinciple currentUser = AuthenticationContextUtils.get();
            log.info("当前登录用户：{}", currentUser);
            Asserts.isTrue(currentUser == null, new BusinessException("-1", "请先登录"));

            // 2. 权限检查
            Asserts.isTrue(!Roles.ADMIN.equals(currentUser.getRole()) && !Roles.SHOP.equals(currentUser.getRole()), 
                          new BusinessException("-2", "无权限操作"));

            // 3. 参数校验
            validateCouponParam(param);

            // 4. 封装数据
            CouponPO couponPO = new CouponPO();
            BeanUtils.copyProperties(param, couponPO);
            
            // 设置商家ID
            if (Roles.SHOP.equals(currentUser.getRole())) {
                // 临时使用默认商家ID，避免Dubbo调用问题
                couponPO.setBusinessId(1);
                log.info("设置商家ID为：1");
            } else {
                // 管理员可以指定商家ID
                Asserts.isTrue(param.getBusinessId() == null, new BusinessException("-3", "管理员必须指定商家ID"));
                couponPO.setBusinessId(param.getBusinessId());
            }

            // 设置时间字段
            if (param.getStartDate() != null && param.getEndDate() != null) {
                try {
                    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                    couponPO.setStartDate(sdf.parse(param.getStartDate()));
                    couponPO.setEndDate(sdf.parse(param.getEndDate()));
                    log.info("设置时间字段 - 开始时间：{}，结束时间：{}", param.getStartDate(), param.getEndDate());
                } catch (ParseException e) {
                    throw new BusinessException("-5", "时间格式错误，请使用 yyyy-MM-dd HH:mm:ss 格式");
                }
            }

            // 设置默认值
            couponPO.setStatus(1); // 1表示激活状态
            couponPO.setEnableStatus(1); // 1表示启用
            couponPO.setCreateAt(new Date());
            couponPO.setUpdateAt(new Date());

            log.info("准备保存的优惠券数据：{}", couponPO);

            // 5. 保存
            couponRepository.save(couponPO);

            log.info("保存优惠券成功，ID：{}", couponPO.getId());
            log.info("=== 保存优惠券完成 ===");

        } catch (BusinessException e) {
            log.error("保存优惠券业务异常：{}", e.getMessage(), e);
            throw e;
        } catch (Exception e) {
            log.error("保存优惠券系统异常：{}", e.getMessage(), e);
            throw new BusinessException("-999", "系统异常：" + e.getMessage());
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void update(CouponParam param) throws BusinessException {
        log.debug("更新优惠券，入参：{}", param);

        // 1. 检查当前登录用户
        UserPrinciple currentUser = AuthenticationContextUtils.get();
        Asserts.isTrue(currentUser == null, new BusinessException("-1", "请先登录"));

        // 2. 权限检查
        Asserts.isTrue(!Roles.ADMIN.equals(currentUser.getRole()) && !Roles.SHOP.equals(currentUser.getRole()), 
                      new BusinessException("-2", "无权限操作"));

        // 3. 检查优惠券是否存在
        CouponPO existingCoupon = couponRepository.getById(param.getId());
        Asserts.isTrue(existingCoupon == null, new BusinessException("-3", "优惠券不存在"));

        // 4. 权限检查（商家只能修改自己的优惠券）
        if (Roles.SHOP.equals(currentUser.getRole())) {
            Integer businessId = getBusinessIdFromUser(currentUser);
            Asserts.isTrue(!businessId.equals(existingCoupon.getBusinessId()), 
                          new BusinessException("-4", "无权限修改该优惠券"));
        }

        // 5. 封装数据
        CouponPO couponPO = new CouponPO();
        BeanUtils.copyProperties(param, couponPO);

        // 6. 更新
        couponRepository.update(couponPO);

        log.debug("更新优惠券成功，ID：{}", couponPO.getId());
    }

    /**
     * 从用户信息中获取商家ID
     * 通过调用用户服务获取管理员的商家ID
     */
    private Integer getBusinessIdFromUser(UserPrinciple user) {
        log.debug("开始获取用户商家ID，用户ID：{}", user.getId());
        try {
            // 调用用户服务获取管理员信息
            AdminDto adminDto = userApi.getAdminById(user.getId());
            log.debug("调用UserApi.getAdminById成功，返回结果：{}", adminDto);
            if (adminDto != null && adminDto.getBusinessId() != null) {
                log.debug("获取到商家ID：{}", adminDto.getBusinessId());
                return adminDto.getBusinessId();
            } else {
                log.warn("AdminDto为空或businessId为空，AdminDto：{}", adminDto);
            }
        } catch (Exception e) {
            log.error("获取用户商家ID失败，用户ID：{}，错误：{}", user.getId(), e.getMessage(), e);
        }
        // 如果获取失败，返回默认值1（临时处理）
        log.warn("使用默认商家ID：1");
        return 1;
    }

    /**
     * 校验优惠券参数
     */
    private void validateCouponParam(CouponParam param) throws BusinessException {
        // 校验时间格式和逻辑
        if (param.getStartDate() != null && param.getEndDate() != null) {
            try {
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                Date startDate = sdf.parse(param.getStartDate());
                Date endDate = sdf.parse(param.getEndDate());
                
                if (startDate.after(endDate)) {
                    throw new BusinessException("-4", "开始时间不能晚于结束时间");
                }
                
            } catch (ParseException e) {
                throw new BusinessException("-5", "时间格式错误，请使用 yyyy-MM-dd HH:mm:ss 格式");
            }
        }
        
        // 校验优惠券面额
        if (param.getDiscountValue() != null && param.getDiscountValue().compareTo(BigDecimal.ZERO) <= 0) {
            throw new BusinessException("-6", "优惠券面额必须大于0");
        }
        
        // 校验最大抵扣金额
        if (param.getMaxDiscountAmount() != null && param.getMaxDiscountAmount().compareTo(BigDecimal.ZERO) <= 0) {
            throw new BusinessException("-7", "最大抵扣金额必须大于0");
        }
        
        // 校验发行量
        if (param.getMaxUsageLimit() != null && param.getMaxUsageLimit() <= 0) {
            throw new BusinessException("-8", "发行量必须大于0");
        }
    }

    @Override
    public PageResult<UserCouponsVO> getUserCouponList(UserCouponQuery query) throws BusinessException {
        log.info("=== 开始查询用户优惠券列表 ===");
        log.info("查询用户优惠券列表，入参：{}", query);

        try {
            // 1. 检查当前登录用户
            UserPrinciple currentUser = AuthenticationContextUtils.get();
            log.info("当前登录用户：{}", currentUser);
            Asserts.isTrue(currentUser == null, new BusinessException("-1", "请先登录"));

            // 2. 设置用户ID（小程序用户只能查看自己的优惠券）
            query.setUserId(currentUser.getId());
            log.info("设置用户ID为：{}", currentUser.getId());

            // 3. 查询用户优惠券列表
            PageInfo<UserCouponsPO> pageInfo = userCouponsRepository.pageList(query);
            log.info("查询到用户优惠券数据：{}", pageInfo);

            // 4. 转换为VO并返回
            PageResult<UserCouponsVO> voPage = new PageResult<>();
            voPage.setPageNo(pageInfo.getPageNum());
            voPage.setPageSize(pageInfo.getPageSize());
            voPage.setTotal(pageInfo.getTotal());

            List<UserCouponsVO> vos = null;
            List<UserCouponsPO> pos = pageInfo.getList();
            if (CollectionUtils.isNotEmpty(pos)) {
                vos = pos.stream().map(po -> {
                    UserCouponsVO vo = new UserCouponsVO();
                    BeanUtils.copyProperties(po, vo);
                    // 设置优惠券名称（从关联查询中获取）
                    // 注意：这里需要从SQL查询结果中获取coupon_name
                    return vo;
                }).collect(Collectors.toList());
            }
            voPage.setObjects(vos);

            log.info("查询用户优惠券列表成功，出参：{}", voPage);
            log.info("=== 查询用户优惠券列表完成 ===");
            return voPage;

        } catch (BusinessException e) {
            log.error("查询用户优惠券列表业务异常：{}", e.getMessage(), e);
            throw e;
        } catch (Exception e) {
            log.error("查询用户优惠券列表系统异常：{}", e.getMessage(), e);
            throw new BusinessException("-999", "系统异常：" + e.getMessage());
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void receiveCoupon(UserCouponsParam param) throws BusinessException {
        log.info("=== 开始领取优惠券 ===");
        log.info("领取优惠券，入参：{}", param);

        try {
            // 1. 检查当前登录用户
            UserPrinciple currentUser = AuthenticationContextUtils.get();
            log.info("当前登录用户：{}", currentUser);
            Asserts.isTrue(currentUser == null, new BusinessException("-1", "请先登录"));

            // 2. 参数校验
            if (param.getCouponId() == null) {
                throw new BusinessException("-2", "优惠券ID不能为空");
            }

            // 3. 使用Redis分布式锁防止并发领取
            String lockKey = "coupon:receive:lock:" + param.getCouponId() + ":" + currentUser.getId();
            String lockValue = UUID.randomUUID().toString();
            
            Boolean lockAcquired = redisTemplate.opsForValue().setIfAbsent(lockKey, lockValue, Duration.ofSeconds(30));
            if (!lockAcquired) {
                throw new BusinessException("-3", "领取过于频繁，请稍后再试");
            }

            try {
                // 4. 查询优惠券信息
                CouponPO coupon = couponRepository.getById(param.getCouponId());
                Asserts.isTrue(coupon == null, new BusinessException("-4", "优惠券不存在"));
                log.info("查询到优惠券信息：{}", coupon);

                // 4.1. 检查活动信息（如果传入了活动ID）
                if (param.getActivityId() != null) {
                    log.info("开始检查活动信息，活动ID：{}", param.getActivityId());
                    
                    try {
                        // 调用活动服务获取活动详情
                        ActivityDto activity = activityApi.getActivityById(param.getActivityId());
                        Asserts.isTrue(activity == null, new BusinessException("-11", "活动不存在"));
                        log.info("查询到活动信息：{}", activity);

                        // 检查活动状态 (对应流程图中的 "校验活动状态 -> 启用状态")
                        Asserts.isTrue(activity.getEnableStatus() != 1, new BusinessException("-12", "活动已禁用"));

                        // 检查活动时间 (对应流程图中的 "参与活动时间是否在活动的有效期")
                        Date now = new Date();
                        Asserts.isTrue(activity.getStartDate() != null && now.before(activity.getStartDate()),
                                     new BusinessException("-13", "活动尚未开始"));
                        Asserts.isTrue(activity.getEndDate() != null && now.after(activity.getEndDate()),
                                     new BusinessException("-14", "活动已过期"));

                        // 检查目标人群是否为空 (对应流程图中的 "目标人群是否为空")
                        if (activity.getTargetCustomer() != null && !activity.getTargetCustomer().trim().isEmpty()) {
                            log.info("活动 {} 设置了目标人群：{}", param.getActivityId(), activity.getTargetCustomer());
                            
                            // 检查用户是否能参与活动 (对应流程图中的 "检查用户是否能参与活动")
                            // 调用用户服务查询用户所属人群
                            // 这里需要传入businessId，可以从优惠券信息中获取
                            Integer businessId = coupon.getBusinessId();
                            List<Integer> userGroupIds = userApi.getUserGroupIds(currentUser.getId(), businessId);
                            log.info("用户 {} 在商家 {} 所属人群：{}", currentUser.getId(), businessId, userGroupIds);
                            
                            // 判断用户信息是否是活动目标人群
                            boolean isTargetUser = checkUserInTargetGroups(userGroupIds, activity.getTargetCustomer());
                            Asserts.isTrue(!isTargetUser, new BusinessException("-15", "您不在活动目标人群中"));
                            log.info("用户 {} 通过活动目标人群验证", currentUser.getId());
                        } else {
                            log.info("活动 {} 未设置目标人群，所有用户可参与", param.getActivityId());
                        }
                    } catch (Exception e) {
                        log.warn("活动验证失败，跳过活动验证：{}", e.getMessage());
                        // 如果活动服务不可用，跳过活动验证
                    }
                }

                // 5. 检查优惠券状态
                Asserts.isTrue(coupon.getStatus() != 1, new BusinessException("-5", "优惠券未激活"));
                Asserts.isTrue(coupon.getEnableStatus() != 1, new BusinessException("-6", "优惠券已禁用"));

                // 6. 检查优惠券有效期
                Date now = new Date();
                Asserts.isTrue(coupon.getStartDate() != null && now.before(coupon.getStartDate()), 
                             new BusinessException("-7", "优惠券尚未开始"));
                Asserts.isTrue(coupon.getEndDate() != null && now.after(coupon.getEndDate()), 
                             new BusinessException("-8", "优惠券已过期"));

                // 7. 检查库存
                Asserts.isTrue(coupon.getUsageNum() >= coupon.getMaxUsageLimit(), 
                             new BusinessException("-9", "优惠券已领完"));

                // 8. 检查用户是否已达到领取上限
                if (coupon.getUsageLimit() > 0) {
                    // 查询用户已领取数量
                    UserCouponQuery userQuery = new UserCouponQuery();
                    userQuery.setUserId(currentUser.getId());
                    userQuery.setCouponId(param.getCouponId());
                    userQuery.setPageNo(1);
                    userQuery.setPageSize(1000); // 查询所有记录
                    
                    PageInfo<UserCouponsPO> userCoupons = userCouponsRepository.pageList(userQuery);
                    int userReceivedCount = (int) userCoupons.getTotal();
                    
                    Asserts.isTrue(userReceivedCount >= coupon.getUsageLimit(), 
                                 new BusinessException("-10", "您已达到该优惠券的领取上限"));
                    log.info("用户已领取数量：{}，限制数量：{}", userReceivedCount, coupon.getUsageLimit());
                }

                // 9. 生成优惠券码
                String couponCode = generateCouponCode(param.getCouponId(), currentUser.getId());
                log.info("生成优惠券码：{}", couponCode);

                // 10. 保存用户优惠券记录
                UserCouponsPO userCouponsPO = new UserCouponsPO();
                userCouponsPO.setUserId(currentUser.getId());
                userCouponsPO.setCouponId(param.getCouponId());
                userCouponsPO.setReceiveChannel(param.getReceiveChannel() != null ? param.getReceiveChannel() : 1);
                userCouponsPO.setStatus(0); // 0-已领取但未使用
                userCouponsPO.setCreateAt(new Date());
                userCouponsPO.setUpdateAt(new Date());
                userCouponsPO.setActivityId(param.getActivityId());
                userCouponsPO.setCouponCode(couponCode);
                userCouponsPO.setCouponType(coupon.getCouponType());
                userCouponsPO.setCouponValue(coupon.getDiscountValue());
                userCouponsPO.setShopId(param.getShopId() != null ? param.getShopId() : coupon.getBusinessId());

                userCouponsRepository.save(userCouponsPO);
                log.info("保存用户优惠券记录成功：{}", userCouponsPO);

                // 11. 更新优惠券使用数量
                coupon.setUsageNum(coupon.getUsageNum() + 1);
                coupon.setUpdateAt(new Date());
                couponRepository.update(coupon);
                log.info("更新优惠券使用数量成功，当前使用数量：{}", coupon.getUsageNum());

                log.info("领取优惠券成功，优惠券码：{}", couponCode);
                log.info("=== 领取优惠券完成 ===");

            } finally {
                // 释放分布式锁
                String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
                redisTemplate.execute(new org.springframework.data.redis.core.script.DefaultRedisScript<>(script, Long.class), 
                                    List.of(lockKey), lockValue);
                log.info("释放分布式锁：{}", lockKey);
            }

        } catch (BusinessException e) {
            log.error("领取优惠券业务异常：{}", e.getMessage(), e);
            throw e;
        } catch (Exception e) {
            log.error("领取优惠券系统异常：{}", e.getMessage(), e);
            throw new BusinessException("-999", "系统异常：" + e.getMessage());
        }
    }

    /**
     * 生成优惠券码
     */
    private String generateCouponCode(Integer couponId, Integer userId) {
        // 格式：COUPON + 优惠券ID + 用户ID + 时间戳后6位 + 随机数2位
        long timestamp = System.currentTimeMillis();
        String timeStr = String.valueOf(timestamp).substring(7); // 取后6位
        String randomStr = String.format("%02d", (int)(Math.random() * 100)); // 2位随机数
        
        return String.format("COUPON%03d%03d%s%s", couponId, userId, timeStr, randomStr);
    }

    /**
     * 检查用户是否在目标人群中
     * @param userGroupIds 用户所属人群ID列表
     * @param targetCustomer 活动目标人群配置（逗号分隔的人群ID）
     * @return true-用户在目标人群中，false-用户不在目标人群中
     */
    private boolean checkUserInTargetGroups(List<Integer> userGroupIds, String targetCustomer) {
        if (userGroupIds == null || userGroupIds.isEmpty()) {
            log.warn("用户未设置所属人群");
            return false;
        }
        
        if (targetCustomer == null || targetCustomer.trim().isEmpty()) {
            log.info("活动未设置目标人群，所有用户可参与");
            return true;
        }
        
        // 解析目标人群ID列表
        List<Integer> targetGroupIds = java.util.Arrays.stream(targetCustomer.split(","))
                .map(String::trim)
                .filter(s -> !s.isEmpty())
                .map(Integer::parseInt)
                .collect(Collectors.toList());
        
        if (targetGroupIds.isEmpty()) {
            log.info("活动目标人群配置为空，所有用户可参与");
            return true;
        }
        
        // 检查用户所属人群是否与目标人群有交集
        boolean isTargetUser = userGroupIds.stream().anyMatch(targetGroupIds::contains);
        log.info("用户人群：{}，目标人群：{}，是否匹配：{}", userGroupIds, targetGroupIds, isTargetUser);
        
        return isTargetUser;
    }

    @Override
    public UserCouponsVO getCouponQrCode(UserCouponCodeQuery query) throws BusinessException {
        log.info("=== 开始查看优惠券二维码 ===");
        log.info("查看优惠券二维码，入参：{}", query);

        try {
            // 1. 检查当前登录用户
            UserPrinciple currentUser = AuthenticationContextUtils.get();
            log.info("当前登录用户：{}", currentUser);
            Asserts.isTrue(currentUser == null, new BusinessException("-1", "请先登录"));

            // 2. 参数校验
            if (query.getCouponCode() == null || query.getCouponCode().trim().isEmpty()) {
                throw new BusinessException("-2", "优惠券编码不能为空");
            }

            // 3. 通过优惠券编码查询用户领取详情
            UserCouponsPO userCouponsPO = userCouponsRepository.getUserCouponByCode(query.getCouponCode());
            Asserts.isTrue(userCouponsPO == null, new BusinessException("-3", "优惠券不存在或已失效"));
            log.info("查询到用户优惠券记录：{}", userCouponsPO);

            // 4. 验证用户权限（只能查看自己的优惠券）
            if (query.getUserId() != null) {
                if (!query.getUserId().equals(userCouponsPO.getUserId())) {
                    throw new BusinessException("-4", "无权限查看该优惠券");
                }
            } else {
                // 如果没有传入userId，则验证当前登录用户
                if (!currentUser.getId().equals(userCouponsPO.getUserId())) {
                    throw new BusinessException("-4", "无权限查看该优惠券");
                }
            }

            // 5. 检查优惠券状态
            if (userCouponsPO.getStatus() == 1) {
                throw new BusinessException("-5", "优惠券已使用");
            }
            if (userCouponsPO.getStatus() == 2) {
                throw new BusinessException("-6", "优惠券已过期");
            }

            // 6. 检查Redis中是否已有二维码缓存
            String qrCacheKey = "coupon:qr:code:" + query.getCouponCode();
            AttachQrDTO cachedQrCode = (AttachQrDTO) redisTemplate.opsForValue().get(qrCacheKey);
            
            if (cachedQrCode != null) {
                log.info("从Redis缓存中获取到二维码：{}", cachedQrCode);
                // 转换为VO并返回
                UserCouponsVO userCouponsVO = convertToUserCouponsVO(userCouponsPO);
                userCouponsVO.setCouponUrl(cachedQrCode.getUrl());
                log.info("查看优惠券二维码成功（缓存），出参：{}", userCouponsVO);
                log.info("=== 查看优惠券二维码完成 ===");
                return userCouponsVO;
            }

            // 7. 调用附件服务生成二维码
            AttachQrDTO qrCode = null;
            try {
                // 传递用户信息到Dubbo上下文
                RpcContext.getContext().setAttachment("userInfo", JsonUtils.toJson(currentUser));
                log.info("已传递用户信息到Dubbo上下文，用户ID：{}", currentUser.getId());
                
                AttachQRParam qrParam = new AttachQRParam();
                qrParam.setContent(query.getCouponCode()); // 二维码内容为优惠券编码
                qrParam.setBusinessType(3); // 假设3为优惠券业务类型
                qrParam.setBusinessId(userCouponsPO.getId()); // 使用用户优惠券记录ID作为业务ID

                qrCode = attachApi.generateQrCode(qrParam);
                if (qrCode == null) {
                    log.warn("附件服务返回的二维码为空");
                    qrCode = new AttachQrDTO();
                    qrCode.setUrl(""); // 设置空URL，避免前端报错
                } else {
                    log.info("生成二维码成功：{}", qrCode);
                    // 8. 将二维码信息缓存到Redis（有效期24小时）
                    redisTemplate.opsForValue().set(qrCacheKey, qrCode, Duration.ofHours(24));
                    log.info("二维码信息已缓存到Redis，key：{}", qrCacheKey);
                }
            } catch (Exception e) {
                log.error("调用附件服务生成二维码失败：{}", e.getMessage(), e);
                // 如果附件服务不可用，返回空的二维码URL，避免接口崩溃
                qrCode = new AttachQrDTO();
                qrCode.setUrl(""); // 设置空URL，避免前端报错
            } finally {
                // 清理Dubbo上下文
                RpcContext.getContext().clearAttachments();
                log.info("已清理Dubbo上下文");
            }

            // 9. 转换为VO并返回
            UserCouponsVO userCouponsVO = convertToUserCouponsVO(userCouponsPO);
            userCouponsVO.setCouponUrl(qrCode.getUrl());
            
            log.info("查看优惠券二维码成功，出参：{}", userCouponsVO);
            log.info("=== 查看优惠券二维码完成 ===");
            return userCouponsVO;

        } catch (BusinessException e) {
            log.error("查看优惠券二维码业务异常：{}", e.getMessage(), e);
            throw e;
        } catch (Exception e) {
            log.error("查看优惠券二维码系统异常：{}", e.getMessage(), e);
            throw new BusinessException("-999", "系统异常：" + e.getMessage());
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void verifyCoupon(String couponCode) throws BusinessException {
        log.info("=== 开始优惠券核销 ===");
        log.info("优惠券核销，优惠券编码：{}", couponCode);

        try {
            // 1. 检查当前登录用户
            UserPrinciple currentUser = AuthenticationContextUtils.get();
            log.info("当前登录用户：{}", currentUser);
            Asserts.isTrue(currentUser == null, new BusinessException("-1", "请先登录"));

            // 2. 参数校验
            Asserts.isTrue(couponCode == null || couponCode.trim().isEmpty(), 
                         new BusinessException("-2", "优惠券编码不能为空"));

            // 3. 通过优惠券编码查询用户领取详情
            UserCouponsPO userCouponsPO = userCouponsRepository.getUserCouponByCode(couponCode);
            Asserts.isTrue(userCouponsPO == null, new BusinessException("-3", "优惠券不存在或已失效"));
            log.info("查询到用户优惠券记录：{}", userCouponsPO);

            // 4. 检查优惠券状态
            Asserts.isTrue(userCouponsPO.getStatus() == 1, new BusinessException("-4", "优惠券已使用，无法重复核销"));
            Asserts.isTrue(userCouponsPO.getStatus() == 2, new BusinessException("-5", "优惠券已过期，无法核销"));

            // 5. 检查优惠券是否在有效期内
            CouponPO coupon = couponRepository.getById(userCouponsPO.getCouponId());
            Asserts.isTrue(coupon == null, new BusinessException("-6", "优惠券信息不存在"));
            
            Date now = new Date();
            Asserts.isTrue(coupon.getEndDate() != null && now.after(coupon.getEndDate()), 
                         new BusinessException("-7", "优惠券已过期，无法核销"));

            // 6. 使用Redis分布式锁防止并发核销
            String lockKey = "coupon:verify:lock:" + couponCode;
            String lockValue = UUID.randomUUID().toString();
            
            Boolean lockAcquired = redisTemplate.opsForValue().setIfAbsent(lockKey, lockValue, Duration.ofSeconds(30));
            if (!lockAcquired) {
                throw new BusinessException("-8", "核销过于频繁，请稍后再试");
            }

            try {
                // 7. 再次检查优惠券状态（防止并发问题）
                UserCouponsPO freshUserCouponsPO = userCouponsRepository.getUserCouponByCode(couponCode);
                Asserts.isTrue(freshUserCouponsPO.getStatus() == 1, new BusinessException("-4", "优惠券已使用，无法重复核销"));

                // 8. 更新优惠券状态为已使用
                freshUserCouponsPO.setStatus(1); // 1-已使用
                freshUserCouponsPO.setUsedTime(new Date());
                freshUserCouponsPO.setUpdateAt(new Date());
                userCouponsRepository.update(freshUserCouponsPO);
                log.info("优惠券状态更新为已使用成功");

                // 9. 清除Redis中的二维码缓存
                String qrCacheKey = "coupon:qr:code:" + couponCode;
                redisTemplate.delete(qrCacheKey);
                log.info("已清除Redis中的二维码缓存，key：{}", qrCacheKey);

                log.info("优惠券核销成功，优惠券编码：{}", couponCode);
                log.info("=== 优惠券核销完成 ===");

            } finally {
                // 释放分布式锁
                String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
                redisTemplate.execute(new org.springframework.data.redis.core.script.DefaultRedisScript<>(script, Long.class), 
                                    List.of(lockKey), lockValue);
                log.info("释放分布式锁：{}", lockKey);
            }

        } catch (BusinessException e) {
            log.error("优惠券核销业务异常：{}", e.getMessage(), e);
            throw e;
        } catch (Exception e) {
            log.error("优惠券核销系统异常：{}", e.getMessage(), e);
            throw new BusinessException("-999", "系统异常：" + e.getMessage());
        }
    }

    /**
     * 将UserCouponsPO转换为UserCouponsVO
     */
    private UserCouponsVO convertToUserCouponsVO(UserCouponsPO userCouponsPO) {
        UserCouponsVO userCouponsVO = new UserCouponsVO();
        BeanUtils.copyProperties(userCouponsPO, userCouponsVO);
        return userCouponsVO;
    }
}
